﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

using ProcessIoUtil;

namespace WebVideoEncoder
{
    public partial class MainForm : Form
    {
        private ProcessIoWrapper m_ProcessIoWrapper = null;

        public MainForm()
        {
            InitializeComponent();
        }

        public string RunCmd(string exec, string args)
        {
            string result = "";

            ProcessStartInfo pinfo = new ProcessStartInfo(exec, args);
            pinfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
            pinfo.UseShellExecute = false;
            pinfo.CreateNoWindow = true;
            pinfo.RedirectStandardOutput = true;
            pinfo.RedirectStandardError = true;
            pinfo.StandardOutputEncoding = Encoding.UTF8;
            pinfo.StandardErrorEncoding = Encoding.UTF8;

            using (Process process = Process.Start(pinfo))
            {
                using (StreamReader reader = process.StandardOutput)
                {
                    result += reader.ReadToEnd();
                }

                using (StreamReader reader = process.StandardError)
                {
                    result += reader.ReadToEnd();
                }
            }
            return result;
        }

        public int GetDevices(List<string> videoList, List<string> audioList)
        {
            videoList.Clear();
            audioList.Clear();

            string ret = RunCmd("ffmpeg", "-f dshow -list_devices true -i dummy");
            var lines = ret.Split('\n');
            bool foundVideoInfo = false;
            bool foundAudioInfo = false;
            foreach (var line in lines)
            {
                string info = line.Trim();
                if (info.Contains("[dshow @"))
                {
                    if (!foundVideoInfo)
                    {
                        if (info.Contains("DirectShow video devices"))
                        {
                            foundVideoInfo = true;
                            continue;
                        }
                    }
                    else
                    {
                        if (!foundAudioInfo)
                        {
                            if (info.Contains("DirectShow audio devices"))
                            {
                                foundAudioInfo = true;
                                continue;
                            }

                            if (info.Contains("Alternative name"))
                            {
                                continue;
                            }
                            else
                            {
                                int begin = info.IndexOf('"');
                                int end = info.LastIndexOf('"');
                                if (end > begin && begin > 0)
                                {
                                    string deviceName = info.Substring(begin + 1, end - begin - 1).Trim();
                                    if (deviceName != null && deviceName.Length > 0) videoList.Add(deviceName);
                                }
                            }
                        }
                        else
                        {
                            if (info.Contains("Alternative name"))
                            {
                                continue;
                            }
                            else
                            {
                                int begin = info.IndexOf('"');
                                int end = info.LastIndexOf('"');
                                if (end > begin && begin > 0)
                                {
                                    string deviceName = info.Substring(begin + 1, end - begin - 1).Trim();
                                    if (deviceName != null && deviceName.Length > 0) audioList.Add(deviceName);
                                }
                            }
                        }
                    }
                }

                if (info.Contains("Immediate exit requested")) break;
            }

            return videoList.Count + audioList.Count;

        }

        private void StopRunningProcess()
        {
            try
            {
                if (m_ProcessIoWrapper != null) m_ProcessIoWrapper.StopRunningProcess();
            }
            catch { }
  
            m_ProcessIoWrapper = null;
        }

        private void Log(string text)
        {
            if (mmLogger.Lines.Length > 1024)
            {
                List<string> finalLines = mmLogger.Lines.ToList();
                finalLines.RemoveRange(0, 512);
                mmLogger.Lines = finalLines.ToArray();
            }

            //mmLogger.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] " + text);
            mmLogger.AppendText(text);
            mmLogger.SelectionStart = mmLogger.Text.Length;
            mmLogger.ScrollToCaret();
        }

        public void LogMsg(string msg)
        {
            Invoke((Action)(() =>
            {
                Log(msg);
            }));
        }

        private void OnStdoutTextRead(string text)
        {
            LogMsg(text);
        }

        private void OnStderrTextRead(string text)
        {
            LogMsg(text);
        }

        private void OnProcessExited()
        {
            Invoke((Action)(() =>
            {
                try
                {
                    lock (this)
                    {
                        gbMediaSource.Enabled = true;
                        gbVideoSetting.Enabled = true;
                        gbServerSetting.Enabled = true;

                        btnStart.Enabled = true;
                        btnStop.Enabled = false;

                        notifyIconMain.Icon = notifyIconStop.Icon;
                        notifyIconMain.Text = this.Text + " (" + notifyIconStop.Text + ")";
                    }
                }
                catch { }
            }));
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            cbbResolution.SelectedIndex = 0;
            rbtnDevice.Checked = false;
            cbbCams.Enabled = false;
            cbbMics.Enabled = false;
            rbtnRtmp.Checked = true;
            edtRtmpSource.Enabled = true;

            btnStart.Enabled = true;
            btnStop.Enabled = false;

            bool needAudio = false;

            var appSettings = ConfigurationManager.AppSettings;
            var allKeys = appSettings.AllKeys;

            try
            {
                if (allKeys.Contains("EnableAudio")) needAudio = Convert.ToBoolean(appSettings["EnableAudio"]);
            }
            catch { }

            cbbMics.Visible = needAudio;
            edtAudioServerUrl.Visible = needAudio;
            edtAudioServerUrl.Enabled = needAudio;

            if (!needAudio)
            {
                cbbCams.Width = edtRtmpSource.Width;
                edtVideoServerUrl.Width = edtChannelName.Width;
            }

            if (allKeys.Contains("ChannelName")) edtChannelName.Text = appSettings["ChannelName"];
            if (allKeys.Contains("VideoServerURL")) edtVideoServerUrl.Text = appSettings["VideoServerURL"];

            if (allKeys.Contains("RtmpSource")) edtRtmpSource.Text = appSettings["RtmpSource"];

            if (needAudio && allKeys.Contains("AudioServerURL")) edtAudioServerUrl.Text = appSettings["AudioServerURL"];

            notifyIconMain.Icon = notifyIconStop.Icon;
            notifyIconMain.Text = this.Text + " (" + notifyIconStop.Text + ")";
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (MessageBox.Show("Are you sure you want to quit ?", this.Text, MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                StopRunningProcess();
                e.Cancel = false;
            }
            else
            {
                e.Cancel = true;
            }
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.Hide();
            }
        }

        private void rbtnWebcam_CheckedChanged(object sender, EventArgs e)
        {
            if (rbtnDevice.Checked)
            {
                cbbCams.Enabled = true;
                cbbCams.Items.Clear();

                if (cbbMics.Visible)
                {
                    cbbMics.Enabled = true;
                    cbbMics.Items.Clear();
                }

                List<string> videoList = new List<string>();
                List<string> audioList = new List<string>();

                GetDevices(videoList, audioList);

                foreach (var item in videoList) cbbCams.Items.Add(item);

                if (cbbMics.Visible)
                {
                    foreach (var item in audioList) cbbMics.Items.Add(item);
                }
            }
            else
            {
                cbbCams.Enabled = false;
                cbbCams.Items.Clear();

                if (cbbMics.Visible)
                {
                    cbbMics.Enabled = false;
                    cbbMics.Items.Clear();
                }
            }
        }

        private void rbtnRtmp_CheckedChanged(object sender, EventArgs e)
        {
            edtRtmpSource.Enabled = rbtnRtmp.Checked;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            string input = "";
            string format = "";
            string output = "";

            string size = "";

            if (rbtnDevice.Checked)
            {
                if (cbbCams.SelectedIndex < 0)
                {
                    MessageBox.Show("Please select a webcam.");
                    return;
                }
                else
                {
                    string deviceName = cbbCams.Items[cbbCams.SelectedIndex].ToString();
                    input = "-f dshow -i video=\"" + deviceName + "\"";

                    if (cbbMics.Visible && cbbMics.Enabled && cbbMics.SelectedIndex >= 0)
                    {
                        string audioDevice = cbbMics.Items[cbbMics.SelectedIndex].ToString();
                        input += " -f dshow -i audio=\"" + audioDevice + "\"";
                    }
                }
            }

            if (rbtnRtmp.Checked)
            {
                if (edtRtmpSource.Text.Trim().Length <= 0)
                {
                    MessageBox.Show("Please input the RTMP source.");
                    return;
                }
                else
                {
                    input = "-re -i " + edtRtmpSource.Text.Trim();
                }
            }

            if (input.Length > 0)
            {
                size = cbbResolution.Items[cbbResolution.SelectedIndex].ToString().Trim();
                format = "-s " + size + " -r " + Convert.ToInt32(nudFps.Value);
                format = "-an " + format; // video only
            }

            if (edtChannelName.Text.Trim().Length <= 0)
            {
                MessageBox.Show("Please input the name of your channel.");
                return;
            }

            if (edtVideoServerUrl.Text.Trim().Length <= 0)
            {
                MessageBox.Show("Please input the URL of the live streaming server.");
                return;
            }

            if (size.Length > 0)
            {
                size = size.Replace('x', '/');
                output = "-f mpeg1video " + edtVideoServerUrl.Text.Trim() + "/" + edtChannelName.Text.Trim() + "/" + size;
            }

            if (output.Length > 0)
            {
                bool needAudioOutput = edtAudioServerUrl.Visible && edtAudioServerUrl.Enabled && edtAudioServerUrl.Text.Trim().Length > 0;
                if (needAudioOutput)
                {
                    if (rbtnDevice.Checked)
                    {
                        needAudioOutput = cbbMics.Visible && cbbMics.Enabled && cbbMics.SelectedIndex >= 0;
                    }
                }

                if (needAudioOutput)
                {
                    output += " -vn -acodec libmp3lame -b:a 8 -reservoir 0 -f mp3 " +
                        edtAudioServerUrl.Text.Trim() + "/" + edtChannelName.Text.Trim();
                }
            }

            if (input.Length <= 0 || format.Length <= 0 || output.Length <= 0) return;

            string args = input + " " + format + " " + output;

            //Console.WriteLine(args);

            gbMediaSource.Enabled = false;
            gbVideoSetting.Enabled = false;
            gbServerSetting.Enabled = false;

            btnStart.Enabled = false;


            if (edtVideoServerUrl.Visible && edtVideoServerUrl.Text.Trim().Length > 0)
            {
                try
                {
                    HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(edtVideoServerUrl.Text.Trim());
                    HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
                    using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                    {
                        LogMsg(sr.ReadToEnd());
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Failed to connect to video server: " + ex.Message);
                    gbMediaSource.Enabled = true;
                    gbVideoSetting.Enabled = true;
                    gbServerSetting.Enabled = true;
                    btnStart.Enabled = true;
                    return;
                }
            }

            if (edtAudioServerUrl.Visible && edtAudioServerUrl.Text.Trim().Length > 0)
            {
                try
                {
                    HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(edtAudioServerUrl.Text.Trim());
                    HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
                    using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                    {
                        LogMsg(sr.ReadToEnd());
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Failed to connect to audio server: " + ex.Message);
                    gbMediaSource.Enabled = true;
                    gbVideoSetting.Enabled = true;
                    gbServerSetting.Enabled = true;
                    btnStart.Enabled = true;
                    return;
                }
            }


            try
            {
                StopRunningProcess();

                mmLogger.Clear();

                m_ProcessIoWrapper = new ProcessIoWrapper("ffmpeg", args);
                m_ProcessIoWrapper.OnStandardOutputTextRead = new Action<string>((text) => { OnStderrTextRead(text); });
                m_ProcessIoWrapper.OnStandardErrorTextRead = new Action<string>((text) => { OnStderrTextRead(text); });
                m_ProcessIoWrapper.OnProcessExited = new Action(() => { OnProcessExited(); });
                m_ProcessIoWrapper.StartProcess();

                btnStop.Enabled = true;

                notifyIconMain.Icon = notifyIconStart.Icon;
                notifyIconMain.Text = this.Text + " (" + notifyIconStart.Text + ")";
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                gbMediaSource.Enabled = true;
                gbVideoSetting.Enabled = true;
                gbServerSetting.Enabled = true;
                btnStart.Enabled = true;
            }

        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            if (!btnStop.Enabled) return;

            btnStop.Enabled = false;

            if (m_ProcessIoWrapper != null)
            {
                m_ProcessIoWrapper.WriteStandardInput("q\n");
                //Thread.Sleep(500);
            }

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(500);
            })
            .ContinueWith((_) =>
            {
                Invoke((Action)(() =>
                {
                    gbMediaSource.Enabled = true;
                    gbVideoSetting.Enabled = true;
                    gbServerSetting.Enabled = true;
                    btnStart.Enabled = true;

                    notifyIconMain.Icon = notifyIconStop.Icon;
                    notifyIconMain.Text = this.Text + " (" + notifyIconStop.Text + ")";
                }));
            });

        }

        private void notifyIconMain_DoubleClick(object sender, EventArgs e)
        {
            this.Show();
            this.WindowState = FormWindowState.Normal;
        }

        private void menuNotify_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (e.ClickedItem.Tag.ToString() == "1")
            {
                this.Show();
                this.WindowState = FormWindowState.Normal;
            }
            else if (e.ClickedItem.Tag.ToString() == "2")
            {
                Task.Factory.StartNew(() =>
                    {
                        Invoke((Action)(() =>
                            {
                                this.Close();
                            }));
                    });
                
            }
        }

        
        
    }
}
